﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using Excel = Microsoft.Office.Interop.Excel;

namespace BOW
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        List<string> listWordofBug = new List<string>(); // danh sách toàn bộ bug
        List<string> listText = new List<string>();// danh sách chứa word_stop
        List<string> listValueCell = new List<string>(); // mỗi phần tử của mảng chứa 1 bug report

        Dictionary<string, int> BOW = new Dictionary<string, int>();
        Dictionary<string, int> dic = new Dictionary<string, int>();
        string pathReadExcel = Environment.CurrentDirectory + "\\DataTest\\aaaa1.xlsx";// change name of file excel
        int N;
        int max_f;
        double BR_length;
        double d_avg;
        double k1 = 1.2;
        double b = 0.5;
        private void releaseObject(object obj)
        {
            try
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
                obj = null;
            }
            catch (Exception ex)
            {
                obj = null;
                MessageBox.Show("Unable to release the Object " + ex.ToString());
            }
            finally
            {
                GC.Collect();
            }
        }
        private static readonly char[] delimiters = { ' ', '.', ',', ';', '\'', '_', '-', ':', '!', '?', '(', ')', '<', '>', '=', '*', '/', '[', ']', '{', '}', '\\', '"', '\r', '\n' };

        private void btnBuilBOW_Click(object sender, EventArgs e)
        {
            string ArrayText = ReadExcelBugReport();
            listWordofBug = processWordofBug(ArrayText);

            //ReadBagOfWord();           

            //#region calculation appearance frequency of word

            foreach (string s in listWordofBug)
            {
                string tem = s.ToUpper();
                if (dic.ContainsKey(tem))
                {
                    int val = int.Parse(dic[tem].ToString());
                    dic.Remove(tem);
                    dic.Add(tem, ++val);
                    dic[tem] = dic[tem]++;
                }
                else
                    dic.Add(tem, 1);
            }

            //#endregion

            ///* - |d|: độ dài tính theo từ.Túi từ có 1000 từ, bug report hiện hành có 70 từ trong túi từ. --> |d| = 10;
            //   - dl_avg: là độ dài trung bình (tính trung bình trên toàn bộ bug report)
            //   qi là từ trong 1 bug report đang xét xem có sự
            //   trùng lắp trên tập bug report đang có hay không. Bug report đang xét gọi là
            //   q, tập report đang có gọi là BR. qi thuộc q, còn ti thuộc BR.
            //   - tf(qi,d): gọi là tần suất xuất hiện của từ qi trong tài liệu d.
            //   - tf(ti,BR): gọi là tần suất xuất hiện của ti trong tập tài liệu BR
            //   -df(qi): số tài liệu d có chứa thuật ngữ qi.
            //   - N: tổng số bug report.
            //*/

            //int max_ti = 0;
            ////tìm phần tử có số lần xuất hiện lớn nhất trong toàn bộ bug report đã có (có trong túi từ)
            //foreach (string item in dic.Keys)
            //{
            //    if (int.Parse(dic[item].ToString()) > max_ti)
            //        max_ti = int.Parse(dic[item].ToString());
            //}

            //// calculation idf(qi) :


            //#region calculation idf(ti)

            //// calculation idf(ti) :            
            //Dictionary<string,double> dic_idf_ti = new Dictionary<string,double>();
            //foreach (string item in dic.Keys)
            //{                
            //    double idf_ti = 0;               
            //    int df_ti = 0;               
            //    for (int i = 0; i < listValueCell.Count - 1; i++)
            //    {
            //            if (listValueCell[i].IndexOf(item) != -1)
            //            df_ti++;
            //    }
            //    idf_ti = calculation_idf_ti(N, df_ti);

            //    dic_idf_ti.Add(item, idf_ti);
            //}

            //#endregion

            //#region calculation wi

            ////calculation wi
            //double k = 1.2;
            //double b = 0.5;
            //Dictionary<string, double> dic_wi = new Dictionary<string, double>();
            //foreach (string item in dic.Keys)
            //{
            //    double tf_ti = 0;
            //    tf_ti = double.Parse(dic[item].ToString()) /max_ti;
            //    double wi = double.Parse(dic_idf_ti[item].ToString()) * (tf_ti * (k + 1)) / (tf_ti + k * (1 - b + b * (listWordofBug.Count / (listWordofBug.Count / N))));
            //    //MessageBox.Show(item.ToString());
            //   // MessageBox.Show(wi.ToString());
            //    dic_wi.Add(item, wi);
            //}

            //#endregion



            // write result into text file
            Writefile(listWordofBug);
        }


        /* N: tổng số tài liệu hiện có --- df_ti : số bug report có chứa từ ti
           dem_ti : số lần xuất hiện của từ ti --- max_ti :  số lần xuất hiện lớn nhất của 1 từ ngẫu nhiên trong văn bản
           BRj_length: độ dài của túi từ (|BRj|) --- dl_avg: độ dài trung bình tính trên toàn bộ bug report
         */
        //tính trọng số từ ti đối với 1 bug report BRj -- ct3
        private double calculation_wi(double idf_ti, double tf_ti_BRj, double BRj_length, double dl_avg)
        {
            double wi = (idf_ti * tf_ti_BRj * (k1 + 1)) / (tf_ti_BRj + k1 * (1 - b + b * (BRj_length / dl_avg)));
            return wi;
        }

        //tính tần số từ ti xuất hiện trong 1 bug report BRj
        private double calculation_tf_ti_BRj(double f_ti, double max_f)
        {
            return 0.5 + (0.5 * f_ti / max_f);
            //return f_ti / max_f;
            //return 1 + Math.Log10(f_ti);
            //return k1 + (1 - k1) * f_ti / max_f;
        }

        //tính tần số nghịch đảo của từ ti -- ct4
        private double calculation_idf_ti(int N, double df_ti)
        {
            return Math.Log10((double.Parse(N.ToString()) - df_ti + 0.5) / (df_ti + 0.5));
        }

        //tính dl_avg
        private double calculation_dl_avg(int BR_length, int N)
        {
            return BR_length / N;
        }

        // tính score(q,d)
        private double calculation_score(double idf_qi,double tf_qi,double d_length,double dl_avg)
        {
            double score = (idf_qi * tf_qi * (k1 + 1)) / (tf_qi + k1 * (1 - b + b * (d_length / dl_avg)));
            return score;
        }
        // Bỏ từng trùng trong bug
        private string RemoveDuplicateWords(string text)
        {
            var words = new HashSet<string>();
            text = Regex.Replace(text, "\\w+", m =>
                                 words.Add(m.Value.ToUpperInvariant())
                                     ? m.Value
                                     : String.Empty);
            return text;
        }

        //Lấy danh sách các từ stop-word
        private void GetWordLimited()
        {
            string pathReadlimit = Environment.CurrentDirectory + "\\FileDelimited\\wordlimited.txt";

            string line = "";
            // lay ra mang ki tu
            System.IO.StreamReader file = new System.IO.StreamReader(pathReadlimit);
            while ((line = file.ReadLine()) != null)
            {
                listText.AddRange(line.Split(','));
            }

        }

        // Xử lí chuỗi từ
        private List<string> processWordofBug(string Arraytext)
        {
            GetWordLimited();// gọi file chưa word_stop.
            List<string> listTemp = new List<string>();

            // Đọc chuỗi và đưa vào mảng
            if (!string.IsNullOrEmpty(Arraytext))
            {
                while (Arraytext != string.Empty)
                {
                    listTemp.AddRange(Arraytext.Split(delimiters));
                    Arraytext = "";
                }
            }

            // Xử lí word-stop
            for (int i = 0; i < listTemp.Count; i++)
            {
                string a = listTemp[i];
                for (int j = 0; j < listText.Count; j++)
                {
                    string c = listText[j];
                    if (c == a.ToLower())
                    {
                        listTemp.Remove(a);
                        i--;
                        break;
                    }

                }
            }
            //Dictionary<string, int> temp = new Dictionary<string, int>();
            //foreach(string s in listTemp)
            //{
            //    temp.Add(s, 0);
            //}
            return listTemp;
        }

        // Đọc file excel
        private string ReadExcelBugReport()
        {
            string Arraytext = "";
            string str;
            int rCnt = 0;
            Excel.Application xlApp;
            Excel.Workbook xlWorkBook;
            Excel.Worksheet xlWorkSheet;
            Excel.Range range;

            xlApp = new Excel.Application();
            xlWorkBook = xlApp.Workbooks.Open(pathReadExcel, 0, true, 5, "", "", true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
            xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
            range = xlWorkSheet.UsedRange;

            // get data from Excel
            N = 131;
            for (rCnt = 2; rCnt <= range.Rows.Count; rCnt++)
            {
                str = (string)(range.Cells[rCnt, 12] as Excel.Range).Value2;
                //str = RemoveDuplicateWords(str);
                Arraytext += str + Environment.NewLine;
                listValueCell.Add(str);
            }

            xlWorkBook.Close(true, null, null);
            xlApp.Quit();
            releaseObject(xlWorkSheet);
            releaseObject(xlWorkBook);
            releaseObject(xlApp);

            return Arraytext;
        }

        //Ghi kết quả vào text file
        private void Writefile(List<string> lstColumnName)
        {
            #region write word into file text

            string pathWriteText = Environment.CurrentDirectory + "\\DataTest\\WriteText.txt";
            System.IO.TextWriter tw = new System.IO.StreamWriter(pathWriteText);

            if (lstColumnName.Count != 0)
            {
                string temp = "";
                foreach (string st in dic.Keys)
                {
                    if (temp != "")
                    {
                        temp += "\n";
                        //temp += "\n";
                    }
                    temp += st + ":" + dic[st].ToString();
                }
                tw.WriteLine(temp);
                tw.Close();
            }
            lblview.Text = pathWriteText;

            #endregion
        }

        // Đọc bag of word
        private Dictionary<string, int> ReadBagOfWord()
        {
            string pathBagOfWord = Environment.CurrentDirectory + "\\DataTest\\WriteText.txt";
            string line = "";
            // lay ra mang ki tu
            System.IO.StreamReader file = new System.IO.StreamReader(pathBagOfWord);

            while ((line = file.ReadLine()) != null)
            {
                List<string> bot = new List<string>();
                bot.AddRange(line.Split(':'));
                //bot.AddRange(line.Split(':'));

                BOW.Add(bot[0], int.Parse(bot[1].ToString()));
            }
            //for (int i = 0; i < bot.Count-1; i++)
            //{
            //    string item = "";
            //    int val = 0;
            //    if (i < bot.Count - 2) 
            //    {
            //        item = bot[i];
            //        val = int.Parse(bot[i + 1].ToString());
            //        BOW.Add(item, val);
            //    }                
            //    i++;
            //}
            return BOW;
        }

        private void btnThucHien_Click(object sender, EventArgs e)
        {
            //    List<string> listCurrentBug = new List<string>(); // danh sách chứa bug nhập vô.
            //    listCurrentBug = processWordofBug(txtValue.Text);
            //    Dictionary<string, int> bug = new Dictionary<string, int>();
            //    bug = GetTf_qi(listCurrentBug);
            //    ReadBagOfWord();
            //    //int max_ti = 0;
            //    N = 131;
            //    //foreach (string s in BOW.Keys)
            //    //{
            //    //    if (int.Parse(BOW[s].ToString()) > max_ti)
            //    //        max_ti = int.Parse(BOW[s].ToString());
            //    //}
            //    //int BRj_length = BOW.Count;
            //    //double dl_avg = 12;//BRj_length / N;
            //    Dictionary<string, int> dic_qi = new Dictionary<string, int>();
            //    int d = 0;
            //    int dl_avg = 12;
            //    foreach (string item in bug.Keys) 
            //    {
            //        foreach (string s in BOW.Keys)
            //        {
            //            if (s == item) 
            //            {
            //                d++;
            //                dic_qi.Add(item, int.Parse(bug[item].ToString()));
            //                break;
            //            }
            //        }
            //    }
            //    int max_ti = 0;
            //    foreach (string item in dic_qi.Keys)
            //    {
            //        if (int.Parse(dic_qi[item].ToString()) > max_ti)
            //            max_ti = int.Parse(dic_qi[item].ToString());
            //    }
            //    double s_qd = 0;
            //    foreach (string item in dic_qi.Keys)
            //    {
            //        //private double calculation_wi(int N, double df_ti, int dem_ti, int max_ti, int BRj_length, double dl_avg)
            //        double w_qi = calculation_wi(N, double.Parse(BOW[item].ToString()), int.Parse(dic_qi[item].ToString()), max_ti, d, dl_avg);
            //        s_qd += w_qi;
            //    }

            //    //foreach (string item in BOW.Keys) 
            //    //{                
            //    //    if (listCurrentBug.IndexOf(item) != -1) 
            //    //    {   
            //    //        //private double calculation_wi(int N, double df_ti, int dem_ti, int max_ti, int BRj_length, double dl_avg)   
            //    //        int df_ti = int.Parse(BOW[item].ToString());
            //    //        int dem_ti = int.Parse(BOW[item].ToString());
            //    //        double wi=calculation_wi(N, df_ti, dem_ti, max_ti, BRj_length, dl_avg);
            //    //        dic_wi.Add(item, wi);
            //    //        d++;                    
            //    //    }
            //    //}
            //    //double s_qd = 0;
            //    //foreach (string item in dic_wi.Keys)
            //    //{
            //    //    s_qd += double.Parse(dic_wi[item].ToString());
            //    //}
            //    //MessageBox.Show(s_qd.ToString());
        }

        private Dictionary<string, int> GetTf_qi(List<string> tempBug)
        {
            Dictionary<string, int> result = new Dictionary<string, int>();
            foreach (string s1 in tempBug)
            {
                string s = s1.ToUpper();
                if (result.ContainsKey(s))
                {
                    int val = int.Parse(result[s].ToString());
                    result.Remove(s);
                    result.Add(s, ++val);
                    result[s] = result[s]++;
                }
                else
                    result.Add(s, 1);
            }
            return result;
        }

        private Dictionary<string, double> Create_Vecto(string bug)
        {
            List<string> listCurrentBug = new List<string>(); // danh sách chứa bug nhập vô.
            listCurrentBug = processWordofBug(bug); // xử lý từ
            Dictionary<string, int> dic_bug = new Dictionary<string, int>(); //tu dien chua cac tu va so lan xuat hien cua no trong 1 bug
            dic_bug = GetTf_qi(listCurrentBug); // từ điển chứa từ và số lần xuất hiện của từ trong bug mới nhập      

            //tinh tan suat xuat hien cua tu ti trong toan bug report
            int df_ti = 0; // số bug report có chứa từ item trong toàn bộ bug đã có
            Dictionary<string, double> vecto = new Dictionary<string, double>();


            foreach (string item in dic_bug.Keys)
            {
                df_ti = 0;
                foreach (string s in listValueCell)
                {
                    if (s.IndexOf(item) != -1) //có từ item trong câu s
                        df_ti++;
                }
                int f_ti = 0;//int.Parse(dic_bug[item].ToString());
                foreach (string s in BOW.Keys)
                {
                    if (s == item)
                    {
                        f_ti += int.Parse(BOW[s].ToString()); //số lần xuất hiện của từ item trong toàn bộ bug report
                        double idf_ti = calculation_idf_ti(N, double.Parse(df_ti.ToString())); //tính idf (ti)
                        double tf_ti = calculation_tf_ti_BRj(double.Parse(f_ti.ToString()), double.Parse(max_f.ToString())); // tính  tf (ti,BR)
                        double wi = calculation_wi(idf_ti, tf_ti, BR_length, d_avg); // tính wi dựa vào công thức 4                       
                        vecto.Add(item, wi);
                        break;
                    }
                }

            }
            return vecto;  // vecto trong so
        }

        private bool Compare_Vecto(Dictionary<string, double> X, Dictionary<string, double> Y, double threshold)
        {
            double tongbp_X = 0; // bình phương độ dài vecto X
            double tongbp_Y = 0; // bình phương độ dài vecto Y
            double tich = 0; //tích vô hướng 2 vecto
            double cos = 0; // góc giữa 2 vecto
            List<string> sim = new List<string>(); // chứa những phần tử trùng nhau của X và Y
            foreach (string x in X.Keys)
            {
                foreach (string y in Y.Keys)
                {
                    if (y == x)
                    {
                        sim.Add(x);
                        break;
                    }
                }
            }
            List<double> X_val = new List<double>();
            List<double> Y_val = new List<double>();
            foreach (string item in X.Keys)
            {
                X_val.Add(double.Parse(X[item].ToString()));
            }
            foreach (string item in Y.Keys)
            {
                Y_val.Add(double.Parse(Y[item].ToString()));
            }

            if (sim.Count > X.Count / 2)
            {
                if (X.Count > Y.Count) //nếu số phần tử X nhiều hơn Y thì tính tích vô hướng đến phần tử cuối của Y và ngược lại 
                {
                    for (int i = 0; i < Y.Count; i++)
                    {
                        tich += X_val[i] * Y_val[i];
                    }
                }
                else
                {
                    for (int i = 0; i < X.Count - 1; i++)
                    {
                        tich += X_val[i] * Y_val[i];
                    }
                }

                for (int i = 0; i < X.Count - 1; i++)
                {
                    tongbp_X += X_val[i] * X_val[i];
                }
                for (int i = 0; i < Y.Count - 1; i++)
                {
                    tongbp_Y += Y_val[i] * Y_val[i];
                }
                cos = tich / (Math.Sqrt(tongbp_X) * Math.Sqrt(tongbp_Y)); // hệ số góc giữa 2 vecto X Y
            }
            // MessageBox.Show(cos.ToString());
            if (cos > threshold)  // nếu cos lớn hơn ngưỡng cho phép thì là trùng lặp
                return false;
            return true;
        }

        private void VSM_Click(object sender, EventArgs e)
        {
            
            string ArrayText = ReadExcelBugReport(); //Đọc file excel
            //listWordofBug = processWordofBug(ArrayText);
            ReadBagOfWord();  // Đọc túi từ
            max_f = 0;
           
            foreach (string item in BOW.Keys) //tìm phần tử có số lần xuất hiện nhiều nhất trong toàn bộ bug report
            {
                if (int.Parse(BOW[item].ToString()) > max_f)
                    max_f = int.Parse(BOW[item].ToString());
            }
            BR_length = double.Parse(BOW.Count.ToString()); //Độ dài của tài liệu (túi từ)
            d_avg = BR_length / double.Parse(N.ToString()); // Độ dài trung bình tính trên toàn bộ bug report
            Score(txtValue.Text);

            double threshold = 0.8;  // Ngưỡng xác định trùng lặp

            Dictionary<string, double> bug_new = new Dictionary<string, double>();
            bug_new = Create_Vecto(txtValue.Text); //chuyển bug mới nhập thành vecto

            foreach (string s in listValueCell)
            {
                Dictionary<string, double> bug_old = new Dictionary<string, double>();
                bug_old = Create_Vecto(s);
                bool check = Compare_Vecto(bug_new, bug_old, threshold); // so sánh bug mới nhập và bug cũ
                if (check == false) // nếu lớn hơn  ngưỡng cho phép
                {
                    MessageBox.Show(s.ToString());
                    MessageBox.Show("Duplicated Bug Report");
                    //  break;
                }
            }
            Application.Restart();

        }

        // Tinh Score(q,d)
        private Dictionary<string,double> Score(string q)
        {
            string temp_CurrentBug = "";
            List<string> listCurrentBug = new List<string>(); // danh sách chứa bug nhập vô.
            
            temp_CurrentBug = RemoveDuplicateWords(q);//xu ly tu trung lap
            listCurrentBug = processWordofBug(temp_CurrentBug); // xử lý từ
            Dictionary<string, int> dic_bug = new Dictionary<string, int>(); //tu dien chua cac tu va so lan xuat hien cua no trong 1 bug
            dic_bug = GetTf_qi(listCurrentBug); // từ điển chứa từ và số lần xuất hiện của từ trong bug mới nhập      

            //tinh tan suat xuat hien cua tu ti trong toan bug report
            int df_ti = 0; // số bug report có chứa từ item trong toàn bộ bug đã có
            Dictionary<string, int> GetTf_d = new Dictionary<string, int>();
            Dictionary<string, double> ListResult = new Dictionary<string, double>();
            List<string> temp_bug = new List<string>();
            List<string> temp_Remobug = new List<string>();
            string temp_Rebug = "";
            int tf_qi = 0;//int.Parse(dic_bug[item].ToString());
            int i = 0;
            
            #region Tính Score
            foreach (string d in listValueCell)
            {
                int d_length = 0;
                temp_bug = processWordofBug(d);
                temp_Rebug = RemoveDuplicateWords(d);
                temp_Remobug = processWordofBug(temp_Rebug);
                // xem trong bug đang xét từ t xuất hiện bao nhiêu lần trong tập bug.(tính |d|)
                foreach (string t in temp_Remobug)
                {
                    if (BOW.ContainsKey(t.ToUpper()))
                        d_length++;
                }
                GetTf_d = GetTf_qi(temp_bug);// từ điển chứa từ và số lần xuất hiện của từ trong bug hiện hành
                double score = 0;
                foreach (string ite in temp_Remobug)
                {
                    foreach (string item in dic_bug.Keys)
                    {
                        if (ite.ToUpper() == item.ToUpper())
                        {
                            df_ti = 0;
                            //Xác định từ chứa trong bao nhiêu bug
                            foreach (string s in listValueCell)
                            {
                                if (s.ToUpper().IndexOf(item.ToUpper()) != -1) //có từ item trong câu s
                                    df_ti++;
                            }
                            tf_qi = int.Parse(GetTf_d[item].ToString()); //số lần xuất hiện của từ item trong toàn bộ bug report
                            double idf_qi = calculation_idf_ti(N, double.Parse(df_ti.ToString())); //tính idf (qi)
                            score += calculation_wi(idf_qi, tf_qi, d_length, d_avg); // tính wi dựa vào công thức 4                       
                        }
                    }
                }
                ListResult.Add(d, score);
                i++;
            }
            #endregion

            return ListResult;
            // show ket qua

        }

        private void btnSearch_Click(object sender, EventArgs e)
        {
            string ArrayText = ReadExcelBugReport(); //Đọc file excel
            d_avg = double.Parse(ArrayText.Length.ToString()) / double.Parse(N.ToString()); // Độ dài trung bình tính trên toàn bộ bug report
            ReadBagOfWord();
            //double score_newbug = Score(txtValue.Text);
            Dictionary<string, double> ListResult = new Dictionary<string, double>();
            ListResult = Score(txtValue.Text);

            DataTable dt = new DataTable();
            dt.Columns.Add("Bug Report");

            List<double> value=new List<double>();
            foreach (string s in ListResult.Keys)
            {
                value.Add(double.Parse(ListResult[s].ToString()));
            }
            value.Reverse();
            double[] ar=new double[5];
            ar[0]=value[0];
            ar[1] = value[1];
            ar[2] = value[2];
            ar[3] = value[3];
            ar[4] = value[4];

            for(int i=0;i<4;i++)
            {
                foreach (string s in ListResult.Keys)
                {
                    if (double.Parse(ListResult[s].ToString()) == ar[i])
                    {
                        DataRow row;
                        row = dt.NewRow();
                        row["Bug Report"] = s;
                        dt.Rows.Add(row);
                        break;
                    }
                }
            }
            dataGridView.DataSource = dt;
            dataGridView.Columns["Bug Report"].Width = 300;
            
            
        }
    }
}